package com.linkgie.galaxy.ai.acr.client;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.TextUtils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * ACR (Ai Character Recognition)
 * <p>
 * AI 智能文字识别 客户端；
 */
public class AcrClient {

    public static void main(String[] args) {
        String host = "203.93.121.22";
        int port = 8880;

        String username = "tester";
        String password = "tsabc@123";

        // 应用ID 和 Key ；
        String appId = "1111EawjfFNV2nrdrPUwTd5VCnWtTEhHWbu1Q";
        String secret = "PHXkP6YNEci";
        long timestamp = System.currentTimeMillis();

        // 生成 Authorization 信息；
        String authorization = GalaxyUserAuthenticationGenerator.generateAuthorization(username, password, timestamp,
                appId, secret);

        System.out.println("timestamp: " + timestamp);
        System.out.println("Authorization: " + authorization);

        try {
            // 创建 HTTP 客户端；
            HttpClient client = HttpClientBuilder.create().build();

            System.out.println();
            System.out.println("----------------------[ 登录 AI 服务器 ]-----------------------");
            // 创建登录请求
            HttpPost loginPost = new HttpPost("http://" + host + ":" + port + "/app/login");
            loginPost.setHeader("No-Redirect", "true");
            loginPost.setHeader("Galaxy-AppId", appId);
            loginPost.setHeader("Authorization", authorization);
            loginPost.setHeader("Content-Type", "application/json");

            // 发起调用；
            HttpResponse httpResponse = client.execute(loginPost);

            // 检查 HTTP 状态码；
            if (httpResponse.getStatusLine().getStatusCode() != 200) {
                // 发生错误；
                System.err.println("调用 AI 服务失败，HTTP 响应码: " + httpResponse.getStatusLine().getStatusCode() + " - "
                        + httpResponse.getStatusLine().getReasonPhrase());
                throw new IllegalStateException("调用 AI 服务失败，HTTP 响应码: " + httpResponse.getStatusLine().getStatusCode()
                        + " - " + httpResponse.getStatusLine().getReasonPhrase());
            }

            String responseText = readText(httpResponse, "UTF-8");
            ServiceResponse loginResponse = deserialize(responseText, ServiceResponse.class);
            System.out.println("收到的HTTP回复内容: \r\n" + responseText);

            // 根据 success 字段判断是否登录成功；
            if (!loginResponse.isSuccess()) {
                // 登录失败；
                System.err.println("登录失败，状态码：" + loginResponse.getCode() + " - " + loginResponse.getStatus()
                        + " ； 错误信息: " + loginResponse.getMessage());

                throw new IllegalStateException(
                        "登录失败，状态码：" + loginResponse.getCode() + " - " + loginResponse.getStatus()
                                + " ； 错误信息: " + loginResponse.getMessage());
            }

            // 登录成功，进行服务调用

            System.out.println();
            System.out.println("----------------------[ 开始上传文件 ]-----------------------");

            // 创建上传文件的 HTTP POST 请求；
            HttpPost httpPost = new HttpPost("http://" + host + ":" + port + "/api/ffs/file");

            // 要上传的图片路径；
            File file = new File("/Users/spring/Desktop/身份证-反面-id-card-back.png");
            MultipartEntityBuilder builder = MultipartEntityBuilder.create();
            builder.addBinaryBody("file", file, ContentType.create("multipart/form-data", "UTF-8"), file.getName());
            HttpEntity multipartEntity = builder.build();
            httpPost.setEntity(multipartEntity);

            // 采用已登录成功的 HttpClient 对象发起调用；
            HttpResponse uploadHttpResponse = client.execute(httpPost);

            String uploadResponseText = null;
            if (uploadHttpResponse.getStatusLine().getStatusCode() != 200) {
                // 上传文件失败，发生 HTTP 错误；
                System.err.println("上传文件失败，HTTP 响应码: " + httpResponse.getStatusLine().getStatusCode() + " - "
                        + httpResponse.getStatusLine().getReasonPhrase());
                throw new IllegalStateException("上传文件失败，HTTP 响应码: " + httpResponse.getStatusLine().getStatusCode()
                        + " - " + httpResponse.getStatusLine().getReasonPhrase());
            }

            uploadResponseText = readText(uploadHttpResponse.getEntity().getContent(), "UTF-8");
            System.out.println("收到的HTTP回复内容: " + uploadResponseText);

            ServiceResponse uploadResponse = deserialize(uploadResponseText, ServiceResponse.class);
            // 根据 success 字段判断是否上传成功；
            if (!uploadResponse.isSuccess()) {
                // 上传文件失败；
                System.err.println("上传文件失败，状态码：" + uploadResponse.getCode() + " - " + uploadResponse.getStatus()
                        + " ； 错误信息: " + uploadResponse.getMessage());

                throw new IllegalStateException(
                        "上传文件失败，状态码：" + uploadResponse.getCode() + " - " + uploadResponse.getStatus()
                                + " ； 错误信息: " + uploadResponse.getMessage());
            }

            // 上传成功，获取图片 ID；
            FileEntry uploadedFileEntry = convert(uploadResponse.getData(), FileEntry.class);
            String imageId = uploadedFileEntry.getId();
            System.out.println();
            System.out.println("上传成功，图片Id: " + imageId);

            System.out.println();
            System.out.println("----------------------[ 提交识别任务 ]-----------------------");

            // 创建提交识别任务的 HTTP POST 请求；
            HttpPost acrPost = new HttpPost("http://" + host + ":" + port + "/api/acr/image");

            // 以请求体(RequestBody)的方式，指定要识别图片ID列表，以及采用的识别模式；
            PatternImageGroup patternImageGroup = new PatternImageGroup();
            patternImageGroup.setImageIds(new String[] { imageId });
            patternImageGroup.setPattern("id-card");
            String json = toJson(patternImageGroup);
            byte[] utf8Bytes = json.getBytes("UTF-8");
            ByteArrayEntity entity = new ByteArrayEntity(utf8Bytes, ContentType.APPLICATION_JSON);
            acrPost.setEntity(entity);

            // 采用已登录成功的 HttpClient 对象发起调用；
            HttpResponse crHttpResponse = client.execute(acrPost);

            // 检查 HTTP 状态码；
            if (crHttpResponse.getStatusLine().getStatusCode() != 200) {
                // 提交识别任务失败，发生 HTTP 错误；
                System.err.println("提交识别任务失败，HTTP 响应码: " + crHttpResponse.getStatusLine().getStatusCode() + " - "
                        + crHttpResponse.getStatusLine().getReasonPhrase());
                throw new IllegalStateException("提交识别任务失败，HTTP 响应码: " + crHttpResponse.getStatusLine().getStatusCode()
                        + " - " + crHttpResponse.getStatusLine().getReasonPhrase());
            }

            String crResponseText = readText(crHttpResponse.getEntity().getContent(), "UTF-8");
            System.out.println("收到的HTTP回复内容: " + crResponseText);

            ServiceResponse crResponse = deserialize(crResponseText, ServiceResponse.class);
            // 根据 success 字段判断是否提交识别任务成功；
            if (!crResponse.isSuccess()) {
                // 提交识别任务失败；
                System.err.println("提交识别任务失败，状态码：" + crResponse.getCode() + " - " + crResponse.getStatus()
                        + " ； 错误信息: " + crResponse.getMessage());

                throw new IllegalStateException(
                        "提交识别任务失败，状态码：" + crResponse.getCode() + " - " + crResponse.getStatus()
                                + " ； 错误信息: " + crResponse.getMessage());
            }

            // 解析识别结果；
            ImageAnalyseResultVO imageAnalyseResult = convert(crResponse.getData(), ImageAnalyseResultVO.class);
            // 识别任务Id;
            imageAnalyseResult.getId();
            // 识别返回的内容；

            // 识别结果；
            String imagesContent = imageAnalyseResult.getContent();

            // 规则检测结果；
            RuleMatchVO[] ruleMatches = imageAnalyseResult.getRuleMatches();

            // 打印识别结果；
            System.out.println("识别结果: " + imagesContent);
            // 打印规则检测结果；
            System.out.println("规则检测结果: ");
            for (RuleMatchVO ruleMatch : ruleMatches) {
                System.out.println(
                        "规则名称: " + ruleMatch.getName() + "    匹配结果: " + (ruleMatch.isMatched() ? "匹配" : "不匹配"));
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static String readText(HttpResponse httpResponse, String charset) {
        try {
            String responseText = readText(httpResponse.getEntity().getContent(), "UTF-8");
            return responseText;
        } catch (IOException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

    public static String readText(InputStream in, String charset) {
        try {
            InputStreamReader reader = TextUtils.isBlank(charset) ? new InputStreamReader(in)
                    : new InputStreamReader(in, charset);
            try {
                StringBuilder content = new StringBuilder();
                char[] buffer = new char[64];
                int len = 0;
                while ((len = reader.read(buffer)) > 0) {
                    content.append(buffer, 0, len);
                }
                return content.toString();
            } finally {
                reader.close();
            }
        } catch (IOException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

    public static ServiceResponse convertResponse(HttpResponse httpResponse) {
        String responseText = readText(httpResponse, "UTF-8");
        return deserialize(responseText, ServiceResponse.class);
    }

    public static String toJson(Object object) {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            String json = objectMapper.writeValueAsString(object);
            return json;
        } catch (JsonProcessingException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

    public static <T> T deserialize(String json, Class<T> clazz) {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            T response = objectMapper.readValue(json, clazz);
            return response;
        } catch (IOException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

    public static <T> T convert(JsonNode json, Class<T> clazz) {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        try {
            T response = objectMapper.treeToValue(json, clazz);
            return response;
        } catch (IOException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

    public static class ServiceResponse {
        private boolean success;
        private int code;

        private String status;

        private String message;

        private JsonNode data;

        /**
         * @return the success
         */
        public boolean isSuccess() {
            return success;
        }

        /**
         * @param success the success to set
         */
        public void setSuccess(boolean success) {
            this.success = success;
        }

        /**
         * @return the code
         */
        public int getCode() {
            return code;
        }

        /**
         * @param code the code to set
         */
        public void setCode(int code) {
            this.code = code;
        }

        /**
         * @return the status
         */
        public String getStatus() {
            return status;
        }

        /**
         * @param status the status to set
         */
        public void setStatus(String status) {
            this.status = status;
        }

        /**
         * @return the message
         */
        public String getMessage() {
            return message;
        }

        /**
         * @param message the message to set
         */
        public void setMessage(String message) {
            this.message = message;
        }

        /**
         * @return the data
         */
        public JsonNode getData() {
            return data;
        }

        /**
         * @param data the data to set
         */
        public void setData(JsonNode data) {
            this.data = data;
        }

    }

    /**
     * 文件信息；
     */
    public static class FileEntry {
        private String id;
        private String name;
        private String type;
        private long size;
        private String contentHash;
        private long createdTime;

        /**
         * @return the id
         */
        public String getId() {
            return id;
        }

        /**
         * @param id the id to set
         */
        public void setId(String id) {
            this.id = id;
        }

        /**
         * @return the name
         */
        public String getName() {
            return name;
        }

        /**
         * @param name the name to set
         */
        public void setName(String name) {
            this.name = name;
        }

        /**
         * @return the type
         */
        public String getType() {
            return type;
        }

        /**
         * @param type the type to set
         */
        public void setType(String type) {
            this.type = type;
        }

        /**
         * @return the size
         */
        public long getSize() {
            return size;
        }

        /**
         * @param size the size to set
         */
        public void setSize(long size) {
            this.size = size;
        }

        /**
         * @return the contentHash
         */
        public String getContentHash() {
            return contentHash;
        }

        /**
         * @param contentHash the contentHash to set
         */
        public void setContentHash(String contentHash) {
            this.contentHash = contentHash;
        }

        /**
         * @return the createdTime
         */
        public long getCreatedTime() {
            return createdTime;
        }

        /**
         * @param createdTime the createdTime to set
         */
        public void setCreatedTime(long createdTime) {
            this.createdTime = createdTime;
        }

    }
}
